#ifdef LWIP
#ifdef ARDUINO
#include <Arduino.h>
#include "STM32FreeRTOS.h"
#include "lwip/apps/httpd.h"
#include "flashFs.h"
#ifdef ONLINE_DEBUG
#include "softplc.h"
#include "msgpack.hpp"
#include "ArduinoJson.h"
#endif
#include "multipart_parser.h"
int keyindex;
struct parsedata
{
    int inContentDisposition; // flag for the right header to look for fields
    char *partname;           // field name
    char *filename;           // file name for an upload field
    FlashFile *saveto;        // file to save contents to
};
#if LWIP_HTTPD_SUPPORT_POST
// class jvariant : public JsonVariant
// {
//     // ARDUINOJSON_NAMESPACE::VariantContent dat;
// public:
//     // jvariant(JsonVariant v)
//     // {
//     //     memcpy(&dat,v._data);
//     // }
//     const void *raw()
//     {
//         return _data->_content.asRaw.data;
//     }
// };

#include "multipart_parser.h"
static uint8_t http_post_uri_file_index = -1;
static uint32_t http_post_content_len = 0;
const char *posturls[] = {
    "/upload",
#ifdef ONLINE_DEBUG
    "/SetTraceVariablesList", "/GetTraceVariables"
#endif
};

/*
 * Mulitpart Parser settings
 *
 * read_on_part_data_begin: (nul
 * read_header_name: Content-Disposition: read_header_value: form-data; name="key_name"
 * read_on_headers_complete: (null)
 * read_part_data: form_value			// May be called multiple times if a file
 * read_on_part_data_end: (null)
 * read_on_body_end: (null)
 *
 */
multipart_parser_settings callbacks;
multipart_parser *_parser;
const char *find_header_name(const char *header)
{

#define HEADER_NAME_TITLE "name="
#define HEADER_NAME_TITLE_LEN 5

    if (header != NULL)
    {
        char *header_name_begin = strstr(header, HEADER_NAME_TITLE); // Find name= in Header
        char *header_name = strtok(header_name_begin, "\"");         // Find the first "
        header_name = strtok(NULL, "\"");                            // Go to the last "
#if HTTPD_DEBUG
        core_debug("POST multipart Header Key found: %s\n", header_name);
#endif
        return header_name;
    }
    return NULL;
}
/* Header which contains the Key with the name */
int read_header_name(multipart_parser *parser, const char *at, size_t length)
{
    struct parsedata *data = (struct parsedata *)multipart_parser_get_data(parser);
    data->inContentDisposition = !strncmp(at, "Content-Disposition", length);
    return 0;
}

int read_header_value(multipart_parser *parser, const char *at, size_t length)
{
    struct parsedata *data = (struct parsedata *)multipart_parser_get_data(parser);
    if (data->inContentDisposition)
    {
        char hdrval[length + 1];
        strncpy(hdrval, at, length);
        if (strtok(hdrval, "; "))
        {
            char *tok;
            while ((tok = strtok(0, "; ")))
            {
                char *rquot;
                if (!strncmp(tok, "name=\"", 6) &&
                    ((rquot = strrchr(tok, '"')) > tok + 6))
                {
                    *rquot = 0;
                    free(data->partname);
                    data->partname = (char *)malloc(strlen(tok + 6) + 1);
                    strcpy(data->partname, tok + 6);
                }
                else if (!strncmp(tok, "filename=\"", 10) &&
                         ((rquot = strrchr(tok, '"')) > tok + 10))
                {
                    *rquot = 0;
                    free(data->filename);
                    data->filename = (char *)malloc(strlen(tok + 10) + 1);
                    strcpy(data->filename, tok + 10);
                    if (data->saveto)
                        data->saveto->close();

                    // determine local location, adapt to your needs:
                    // for production code, ADD SANITY CHECKS, the following code
                    // allows an attacker to write any location of your server
                    // with a filename containing relative paths!
                    data->saveto = new FlashFile();
                    data->saveto->open_write(data->partname);
                }
            }
        }
    }
    return 0;
}

/* Value for the latest key */
/* If this is a file, this may be called multiple times. */
/* Wait until part_end for the complete file. */
// char tx[] = "0123456789";
// int inx = 0;
int total = 0;
int read_part_data(multipart_parser *parser, const char *at, size_t length)
{
    int len = length;
    total += len;
    // core_debug("read_part_data: %d,total=%d\n", len, total);
    // for (int i = 0; i < length; i++)
    // {
    //     if (at[i] != tx[inx])
    //     {
    //         core_debug("error :%d", total);
    //     }
    //     inx++;
    //     total++;
    //     if (inx > 9)
    //         inx = 0;
    // }
    struct parsedata *data = (struct parsedata *)multipart_parser_get_data(parser);
    if (data->partname && data->filename)
    {
        char buf[length];
        memcpy(buf, at, length);
        FlashFile *fw = data->saveto;
        if (fw->opened())
        {
            core_debug("read_part_data: filename=%s ,len=%d\n,total=%d", fw->filename, len, total);
            fw->write(buf, len);
        }
    }
    return 0;
}

/* End of header which contains the key */
int read_on_headers_complete(multipart_parser *p)
{
    // #if HTTPD_DEBUG
    // #endif
    // FlashFile *fw = (FlashFile *)p->data;
    // fw->open_write(p->filename);
    // core_debug("read_on_headers_complete: filename=%s\n", fw->filename);
    return 0;
}

/* End of the entire form */
int read_on_body_end(multipart_parser *parser)
{
    struct parsedata *data = (struct parsedata *)multipart_parser_get_data(parser);
    // #if HTTPD_DEBUG
    core_debug("read_on_body_end: fw->close %s\n", data->filename);
    // #endif
    data->saveto->close();
    return 0;
}

static err_t
http_parse_post(char *data, uint32_t length)
{
    // core_debug("http_parse_post POST data: %d\n", length);
    char buf[length];
    memcpy(buf, data, length);
    /* Parse the data */
    multipart_parser_execute(_parser, buf, length);

    return ERR_OK;
} /* Find boundary value in the Content-Type. */
const char *
find_boundary(const char *content_type)
{

#define BOUNDARY_TITLE "boundary="
#define BOUNDARY_TITLE_LEN 9

    if (content_type != NULL)
    {
        char *boundary_begin = strstr(content_type, BOUNDARY_TITLE); // Find Boundary= in Content-Type
        char *boundary = boundary_begin + BOUNDARY_TITLE_LEN;        // Remove the Boundary=
#if HTTPD_DEBUG
        core_debug("POST multipart Boundary found: %s\n", boundary);
#endif

        return boundary;
    }
    return NULL;
} /* Find Header Key Name in the header. */
extern boolean POSTFILE, POSTED;
/** Called when a POST request has been received. The application can decide
 * whether to accept it or not.
 *
 * @param connection Unique connection identifier, valid until httpd_post_end
 *        is called.
 * @param uri The HTTP header URI receiving the POST request.
 * @param http_request The raw HTTP request (the first packet, normally).
 * @param http_request_len Size of 'http_request'.
 * @param content_len Content-Length from HTTP header.
 * @param response_uri Filename of response file, to be filled when denying the
 *        request
 * @param response_uri_len Size of the 'response_uri' buffer.
 * @param post_auto_wnd Set this to 0 to let the callback code handle window
 *        updates by calling 'httpd_post_data_recved' (to throttle rx speed)
 *        default is 1 (httpd handles window updates automatically)
 * @param content_type Content-Type string.
 * @return ERR_OK: Accept the POST request, data may be passed in
 *         another err_t: Deny the POST request, send back 'bad request'.
 */
err_t httpd_post_begin(void *connection,
                       const char *uri,
                       const char *http_request,
                       u16_t http_request_len,
                       int content_len,
                       char *response_uri,
                       u16_t response_uri_len,
                       u8_t *post_auto_wnd,
                       const char *content_type)
{
    *post_auto_wnd = 0;
    int dd = 50;
    // Check the URI given with the list
    for (uint8_t i = 0; i < sizeof(posturls) / 4; i++)
    {
        if (strcmp(uri, posturls[i]) == 0)
        {

            http_post_uri_file_index = i;
            http_post_content_len = content_len;
            const char *boundary = find_boundary(content_type);
            switch (http_post_uri_file_index)
            {
            case 0:

                while ((!POSTED) && dd--)
                {
                    POSTFILE = 1;
                    // vTaskDelay(1);
                }
#if HTTPD_DEBUG
                core_debug("httpd_post_begin: Post Content: %s\n", http_request);
#endif

                memset(&callbacks, 0, sizeof(multipart_parser_settings));

                callbacks.on_header_field = read_header_name;
                callbacks.on_header_value = read_header_value;
                callbacks.on_part_data = read_part_data;
                callbacks.on_headers_complete = read_on_headers_complete;
                callbacks.on_body_end = read_on_body_end;

                /*
			 * Get the boundary from the content-type
			 * Then pass it to the parser
			 */
                if (boundary != NULL)
                {
                    static struct parsedata data = {0};
                    memset(&data, 0, sizeof(data));
                    _parser = multipart_parser_init(boundary, &callbacks);
                    multipart_parser_set_data(_parser, &data);
                }

                return ERR_OK;
            case 1:
                return ERR_OK;
            case 2:

                return ERR_OK;
            }
        }
        //returns /404.html when response_uri is empty
    }
    return ERR_VAL;
}
/** Called for each pbuf of data that has been received for a POST.
 * ATTENTION: The application is responsible for freeing the pbufs passed in!
 *
 * @param connection Unique connection identifier.
 * @param p Received data.
 * @return ERR_OK: Data accepted.
 *         another err_t: Data denied, http_post_get_response_uri will be called.
 */
err_t httpd_post_receive_data(void *connection, struct pbuf *p)
{

    char *data;
    err_t ret_val = ERR_ARG;
    struct http_state *hs = (struct http_state *)connection;
    struct pbuf *q = p;
    while (p != NULL)
    {
        switch (http_post_uri_file_index)
        {
        case 0:
            data = (char *)p->payload;
            ret_val = http_parse_post(data, p->len);

            break;
#ifdef ONLINE_DEBUG
        case 1:
        {

            if (plc_state == Started)
            {
                regVari((const char*) p->payload, p->len);
                hs->file = "/ok.html";
                ret_val = ERR_OK;
                break;
            }
            // core_debug(error.c_str());
            hs->file = "/error.html";
            ret_val = ERR_OK;
        }
        break;
        case 2:
        {

            hs->left = readVari();
            hs->file = stxbuf;
            ret_val = ERR_OK;
        }
        break;
#endif
        default:
            LWIP_ASSERT("CASE", 0);
        }
        httpd_post_data_recved(hs, p->len);
        p = p->next;
    }
    pbuf_free(q);
    return ret_val;
}

/** Called when all data is received or when the connection is closed.
 * The application must return the filename/URI of a file to send in response
 * to this POST request. If the response_uri buffer is untouched, a 404
 * response is returned.
 *
 * @param connection Unique connection identifier.
 * @param response_uri Filename of response file on success
 * @param response_uri_len Size of the 'response_uri' buffer.
 */
int httpd_post_finished(void *connection,
                        char *response_uri,
                        u16_t response_uri_len)
{

    struct http_state *hs = (struct http_state *)connection;
    if (hs != NULL)
    {

        switch (http_post_uri_file_index)
        {
        case 0:
            strcpy(response_uri, "/index.html");

            /* End the parser */
            // free(_parser->data);
            multipart_parser_free(_parser);
            POSTFILE = 0;
            break;
        case 1:
            strcpy(response_uri, hs->file);
            break;
        case 2:
            strcpy(response_uri, "/o.cls"); //用于指定数据格式
            return 2;
        }
    }
    return 0;
}

#endif
#endif
#endif